#!/usr/bin/env python
#-*- coding: ascii -*-

from __future__ import print_function
try:
	from StringIO import StringIO
except:
	from io import StringIO

def Mul8Bit(a, b):
	t = a * b + 128;
	return (t + (t >> 8)) >> 8;
	
def ExtendNTo8Bits(input, n):
	return (input >> (n - (8 - n))) | (input << (8 - n))

def Extend4To8Bits(input):
	return ExtendNTo8Bits(input, 4)

def Extend5To8Bits(input):
	return ExtendNTo8Bits(input, 5)

def Extend6To8Bits(input):
	return ExtendNTo8Bits(input, 6)

def Extend7To8Bits(input):
	return ExtendNTo8Bits(input, 7)

def GenExpand5Table():
	expand5 = [ 0 for i in range(32) ]
	for i in range(len(expand5)):
		expand5[i] = Extend5To8Bits(i)
	return expand5

def GenExpand6Table():
	expand6 = [ 0 for i in range(64) ]
	for i in range(len(expand6)):
		expand6[i] = Extend6To8Bits(i)
	return expand6

def GenExpand7Table():
	expand7 = [ 0 for i in range(128) ]
	for i in range(len(expand7)):
		expand7[i] = Extend7To8Bits(i)
	return expand7

def ETCGetModifier(cw, selector):
	etc1_modifier_table = (
		(2, 8),
		(5, 17),
		(9, 29),
		(13, 42),
		(18, 60),
		(24, 80),
		(33, 106),
		(47, 183),
	)
	if selector >= 2:
		return etc1_modifier_table[cw][selector - 2]
	else:
		return -etc1_modifier_table[cw][not selector]

def ETC1DecodeValue(diff, inten, selector, packed_c):
	if diff:
		c = Extend5To8Bits(packed_c)
	else:
		c = Extend4To8Bits(packed_c)
	c += ETCGetModifier(inten, selector)
	c = max(0, min(c, 255))
	return c

def PrepareOptTable(expand):
	size = len(expand)
	o_match = [ [ 0 for i in range(2) ] for j in range(256) ]
	for i in range(256):
		best_err = 256
		for min in range(size):
			for max in range(size):
				min_e = expand[min]
				max_e = expand[max]
				err = abs(max_e + Mul8Bit(min_e - max_e, 0x55) - i);
				if err < best_err:
					o_match[i][0] = max
					o_match[i][1] = min
					best_err = err
	return o_match

def PrepareOptTable2(expand):
	size = len(expand)
	o_match = [ [ 0 for i in range(2) ] for j in range(256) ]
	for i in range(256):
		best_err = 256
		for min in range(size):
			for max in range(size):
				combo = (43 * expand[min] + 21 * expand[max] + 32) >> 6;
				err = abs(i - combo);
				if err < best_err:
					o_match[i][0] = min
					o_match[i][1] = max
					best_err = err
	return o_match

def PrepareETC1InverseLookup():
	etc1_inverse_lookup = [ [ 0 for i in range(256) ] for j in range(64) ]
	for diff in range(2):
		if diff != 0:
			limit = 32
		else:
			limit = 16
		for inten in range(8):
			for selector in range(4):
				inverse_table_index = diff + (inten << 1) + (selector << 4)
				for color in range(256):
					best_err = 0xFFFFFFFF
					best_packed_c = 0
					for packed_c in range(limit):
						v = ETC1DecodeValue(diff, inten, selector, packed_c);
						err = abs(v - color);
						if err < best_err:
							best_err = err
							best_packed_c = packed_c
							if best_err == 0:
								break;
					etc1_inverse_lookup[inverse_table_index][color] = best_packed_c | (best_err << 8)
	return etc1_inverse_lookup

class Tables:
	def __init__(self):
		self.tables = []

	def Add(self, data_type, name, table, dims):
		self.tables.append((data_type, name, table, dims))

	def WriteToHeaderFile(self, file_name):
		header_str = StringIO()

		header_str.write("// DON'T EDIT THIS FILE. Automatically generated by TableGen.py\n\n")
		header_str.write("namespace KlayGE\n")
		header_str.write("{\n")
		header_str.write("\tnamespace TexCompressionLUT\n")
		header_str.write("\t{\n")

		for table in self.tables:
			if table[3] == 1:
				header_str.write("\t\textern %s %s[%d];\n" % (table[0], table[1], len(table[2])))
			else:
				header_str.write("\t\textern %s %s[%d][%d];\n" % (table[0], table[1], len(table[2]), len(table[2][0])))

		header_str.write("\t}\n")
		header_str.write("}\n")

		try:
			cur_header_file = open(file_name, "r")
			cur_header_str = cur_header_file.read()
			cur_header_file.close()
		except:
			cur_header_str = ""
		new_header_str = header_str.getvalue()
		if new_header_str != cur_header_str:
			output_file = open(file_name, "w")
			output_file.write(new_header_str)
			output_file.close()

	def WriteToSourceFile(self, file_name):
		source_str = StringIO()

		source_str.write("#include <KlayGE/KlayGE.hpp>\n\n")
		source_str.write("// DON'T EDIT THIS FILE. Automatically generated by TableGen.py\n\n")
		source_str.write("namespace KlayGE\n")
		source_str.write("{\n")
		source_str.write("\tnamespace TexCompressionLUT\n")
		source_str.write("\t{\n")

		for n in range(len(self.tables)):
			table = self.tables[n]
			if table[3] == 1:
				source_str.write("\t\t%s %s[%d] =\n" % (table[0], table[1], len(table[2])))
				source_str.write("\t\t{\n")
				source_str.write("\t\t\t")
				for i in range(len(table[2])):
					source_str.write("%d" % table[2][i])
					if i != len(table[2]) - 1:
						source_str.write(",")
					source_str.write(" ")
				source_str.write("\n")
				source_str.write("\t\t};\n")
			else:
				source_str.write("\t\t%s %s[%d][%d] =\n" % (table[0], table[1], len(table[2]), len(table[2][0])))
				source_str.write("\t\t{\n")
				for i in range(len(table[2])):
					source_str.write("\t\t\t{ ")
					for j in range(len(table[2][i])):
						source_str.write("%d" % table[2][i][j])
						if j != len(table[2][i]) - 1:
							source_str.write(",")
						source_str.write(" ")
					source_str.write("}")
					if i != len(table[2]) - 1:
						source_str.write(",")
					source_str.write("\n")
				source_str.write("\t\t};\n")
			if n != len(self.tables) - 1:
				source_str.write("\n")

		source_str.write("\t}\n")
		source_str.write("}\n")

		try:
			cur_source_file = open(file_name, "r")
			cur_source_str = cur_source_file.read()
			cur_source_file.close()
		except:
			cur_source_str = ""
		new_source_str = source_str.getvalue()
		if new_source_str != cur_source_str:
			output_file = open(file_name, "w")
			output_file.write(new_source_str)
			output_file.close()

if __name__ == "__main__":
	expand5 = GenExpand5Table()
	expand6 = GenExpand6Table()
	expand7 = GenExpand7Table()

	quant_5_tab = [ 0 for i in range(256 + 16) ]
	quant_6_tab = [ 0 for i in range(256 + 16) ]
	for i in range(len(quant_5_tab)):
		v = max(0, min(i - 8, 255))
		quant_5_tab[i] = expand5[Mul8Bit(v, len(expand5) - 1)]
		quant_6_tab[i] = expand6[Mul8Bit(v, len(expand6) - 1)]

	o_match5 = PrepareOptTable(expand5)
	o_match6 = PrepareOptTable(expand6)
	o_match7 = PrepareOptTable2(expand7)

	etc1_inverse_lookup = PrepareETC1InverseLookup()

	tables = Tables();
	tables.Add("uint8_t", "EXPAND5", expand5, 1);
	tables.Add("uint8_t", "EXPAND6", expand6, 1);
	tables.Add("uint8_t", "EXPAND7", expand7, 1);
	tables.Add("uint8_t", "O_MATCH5", o_match5, 2);
	tables.Add("uint8_t", "O_MATCH6", o_match6, 2);
	tables.Add("uint8_t", "O_MATCH7", o_match7, 2);
	tables.Add("uint8_t", "QUANT_5_TAB", quant_5_tab, 1);
	tables.Add("uint8_t", "QUANT_6_TAB", quant_6_tab, 1);
	tables.Add("uint16_t", "ETC1_INVERSE_LOOKUP", etc1_inverse_lookup, 2);

	tables.WriteToHeaderFile("Tables.hpp")
	tables.WriteToSourceFile("Tables.cpp")
